package com.android.UrbanRemix;

/**
 * Displays a map with all markers from the database. Handles Preview.
 * @author BD
 * 
 * UI intents and Path rendering
 * @author Ryan
 * 
 * BUGS: 
 * 1) Crashes when user hits back button when previewing. 
 * 2) Possible crash due to projectIndex not being initialized
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.content.Context;
import android.content.Intent;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.util.Config;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;

import de.android1.overlaymanager.ManagedOverlay;
import de.android1.overlaymanager.ManagedOverlayItem;
import de.android1.overlaymanager.MarkerRenderer;
import de.android1.overlaymanager.OverlayManager;
import de.android1.overlaymanager.ZoomEvent;
import de.android1.overlaymanager.ManagedOverlayGestureDetector.OnOverlayGestureListener;

public class Map extends MapActivity implements Runnable {

	private static final String TAG = "Main Map";
	private static final String TagThread = "Communication Thread";
	private static final String serverAddress = "http://urbanremix.lcc.gatech.edu/uploads/";
	private static final String servletAddress = "http://urbanremix.lcc.gatech.edu:8080/urbanremix-webapp/AndroidServlet?";
	// "http://192.168.2.3:8080/AndroidServlet?";

	private static boolean getProjectsFlag = true;
	private List<URProject> projectList;
	// By default the first project in the list is selected
	private int selectedProjectIndex = 0;

	public static int maxNumberOfMarkers = 10;
	public static GeoPoint topLeftLocation;
	public static GeoPoint bottomRightLocation;
	public String filetype = "Sound";

	private MapView myMapView = null;
	private MapController mapControl;
	public static Context cntxt;

	private List<Overlay> mapOverlays;
	private drawLineVector positionOverlay;

	// background thread for communication with servlet
	private Thread communicationThread;

	// Button to load markers from server
	private Button loadMarkersBtn;

	// private drawLineVector positionOverlay;
	Timer t = new Timer();

	OverlayManager overlayManager;
	ManagedOverlay managedOverlay;
	ManagedOverlayItem itemred;
	ArrayList<ManagedOverlayItem> mylist;

	public double myLat = 33.736118;
	public double myLng = -84.371181;

	public double lat2 = 33.936118;
	public double lng2 = -84.571181;
	public Compass compass = new Compass();

	private SensorManager mSensorManager;
	private float[] mValues;
	public CheckBox checkBoxGlobal = null;
	public static CheckBox checkBoxGlobal2 = null;
	public static boolean timerOn = false;
	public SeekBar mSeekBar;
	public static TextView mProgressText;
	public static TextView marker;
	public static Spinner m_myDynamicSpinner;
	public static ArrayAdapter<CharSequence> m_adapterForSpinner;
	public static boolean markerCheckBox = false;

	public static SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() {
		public void onStopTrackingTouch(SeekBar seekBar) {
		}

		public void onStartTrackingTouch(SeekBar seekBar) {
		}

		public void onProgressChanged(SeekBar seekBar, int progress,
				boolean fromUser) {
			maxNumberOfMarkers = progress;
			mProgressText.setText("Load " + progress + " markers");
		}
	};

	// Inflates a menu when the menu button is pressed.
	public boolean onCreateOptionsMenu(Menu menu) {

		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.options_menu, menu);
		MenuItem About = menu.add(0, 0, 0, "About");
		About.setIntent(new Intent(this, AboutUR.class));

		/**
		 * Code to incorporate streetview into the App.
		 */
		/*
		 * // MenuItem streetView = menu.add(0, 1, 1, "StreetView"); Intent
		 * myIntent = new Intent(Intent.ACTION_VIEW, Uri
		 * .parse("google.streetview:cbll=" + myLat + "," + myLng +
		 * "&cbp=1,180,,0,1.0")); // Log.i("ErrorDetect", "Before bundle");
		 * Bundle bundle = new Bundle(); // bundle.putDouble("lat", myLat);
		 * bundle.putDouble("lng", myLng); // myIntent.putExtras(bundle);
		 * Log.i("ErrorDetect", "putExtras"); // streetView.setIntent(myIntent);
		 */

		MenuItem uploadView = menu.add(0, 1, 1, "Upload");
		Intent uploadIntent = new Intent(this, UrbanRemix_New.class);
		uploadView.setIntent(uploadIntent);
		return true;

	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.mainmap);
		myMapView = (MapView) findViewById(R.id.map);
		myMapView.setBuiltInZoomControls(true);
		mapControl = myMapView.getController();
		mapControl.setZoom(3);

		cntxt = this.getApplicationContext();

		loadMarkersBtn = (Button) findViewById(R.id.load);

		topLeftLocation = myMapView.getProjection().fromPixels(0, 0);
		bottomRightLocation = myMapView.getProjection().fromPixels(
				myMapView.getWidth(), myMapView.getHeight());

		loadMarkersBtn.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				// Gather TopLeft and BottomRight information and call server to
				// get markers
				Projection projection = myMapView.getProjection();
				int y = myMapView.getHeight();
				int x = myMapView.getWidth();

				topLeftLocation = projection.fromPixels(0, 0);
				bottomRightLocation = projection.fromPixels(x, y);
				Log.i("TopLeft Location", topLeftLocation.toString());
				Log.i("Bottom Right Location", bottomRightLocation.toString());

				if (communicationThread != null)
					communicationThread = null;
				communicationThread = new Thread(Map.this);
				communicationThread.start();
			}
		});

		loadMarkersBtn.setEnabled(true);

		// create an overlayManager that displays markers on the map
		overlayManager = new OverlayManager(getApplication(), myMapView);

		managedOverlay = overlayManager.createOverlay("DatabaseMarkers",
				getResources().getDrawable(R.drawable.ledorange));

		managedOverlay.setCustomMarkerRenderer(new MarkerRenderer() {

			public Drawable render(ManagedOverlayItem item,
					Drawable defaultMarker, int bitState) {
				if (item.getCustomRenderedDrawable() != null)
					return item.getCustomRenderedDrawable();

				BitmapDrawable b = (BitmapDrawable) defaultMarker;
				Bitmap bitmap = Bitmap.createBitmap(b.getBitmap().copy(
						Bitmap.Config.ARGB_8888, true));
				Canvas canvas = new Canvas(bitmap);
				Paint p = new Paint();

				if (item.getTitle().equals("R")) {
					p.setColor(Color.RED);
					p.setAntiAlias(true);
					p.setTextSize(12);
					canvas.drawText(item.getTitle(), 7, 15, p);
				} else {
					p.setColor(Color.BLACK);
					p.setAntiAlias(true);
					p.setTextSize(10);
					if (item.getTitle().equals("10"))
						canvas.drawText(item.getTitle(), 5, 15, p);
					else
						canvas.drawText(item.getTitle(), 8, 15, p);
				}
				BitmapDrawable bd = new BitmapDrawable(bitmap);
				bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd
						.getIntrinsicHeight());

				item.setCustomRenderedDrawable(bd);
				return bd;
			}
		});

		// code to detect taps on screen and markers.
		managedOverlay
				.setOnOverlayGestureListener(new OnOverlayGestureListener() {

					public boolean onZoom(ZoomEvent zoom, ManagedOverlay overlay) {
						return false;
					}

					public boolean onSingleTap(MotionEvent e,
							ManagedOverlay overlay, GeoPoint point,
							ManagedOverlayItem item) {
						if (item != null)
							Toast.makeText(getApplicationContext(),
									item.getSnippet(), Toast.LENGTH_SHORT)
									.show();
						else
							Toast.makeText(getApplicationContext(),
									point.toString(), Toast.LENGTH_SHORT)
									.show();
						return true;
					}

					public boolean onScrolled(MotionEvent e1, MotionEvent e2,
							float distanceX, float distanceY,
							ManagedOverlay overlay) {
						return false;
					}

					public void onLongPressFinished(MotionEvent e,
							ManagedOverlay overlay, GeoPoint point,
							ManagedOverlayItem item) {

					}

					public void onLongPress(MotionEvent e,
							ManagedOverlay overlay) {

					}

					/**
					 * Image and Sound Preview go here
					 */
					public boolean onDoubleTap(MotionEvent e,
							ManagedOverlay overlay, GeoPoint point,
							ManagedOverlayItem item) {
						if (item != null) { // a marker is double tapped
							if (item.getPoint().equals(itemred.getPoint())) {
								// show address of the reference location or
								// something here.
								Toast.makeText(getApplicationContext(), itemred
										.getPoint().toString(),
										Toast.LENGTH_SHORT);
							} else if (filetype.equals("Sound")
									&& item.getSnippet().contains("wav")) {

								String soundstring = serverAddress
										+ item.getSnippet();
								// Instantiate a Sound Preview Manager Task..
								SoundPreviewManagerTask spmTask = new SoundPreviewManagerTask(
										Map.this);
								/**
								 * Create a PreviewTaskInputPayload for
								 * SoundPreviewManagerTask
								 */
								PreviewTaskInputPayload soundPayload = new PreviewTaskInputPayload(
										soundstring, item.getPoint()
												.getLatitudeE6(), item
												.getPoint().getLongitudeE6());
								spmTask.execute(soundPayload);

							} else if (filetype.equals("Image")
									&& item.getSnippet().contains("jpg")) {

								// Instantiate an Image Preview Manager Task..
								ImagePreviewManagerTask ipmTask = new ImagePreviewManagerTask(
										Map.this);
								String imagestring = serverAddress
										+ item.getSnippet();
								Log.i(TAG, imagestring);
								/**
								 * Create a PreviewTaskInputPayload for
								 * ImagePreviewManagerTask
								 */
								PreviewTaskInputPayload imagePayload = new PreviewTaskInputPayload(
								/* "http://ut2341.com/UT3ClassicLarge.png", */
								imagestring, item.getPoint().getLatitudeE6(),
										item.getPoint().getLongitudeE6());
								ipmTask.execute(imagePayload);
							}
						} else {
							// create a new reference (itemRed) at point
							if (itemred != null) {
								managedOverlay.remove(itemred);
								itemred = null;
							}
							itemred = new ManagedOverlayItem(point, "R",
									"Render Point");
							managedOverlay.add(itemred);
							overlayManager.populate();
						}
						return true;
					}
				});

		/**
		 * Initialize itemRed for render sound to reference this location in the
		 * beginning -- Grant Park
		 */
		itemred = new ManagedOverlayItem(new GeoPoint(33736118, -84371181),
				"R", "Render Point");
		managedOverlay.add(itemred);
		overlayManager.populate();

		// background thread fetches data from servlet.
		communicationThread = new Thread(this);
		communicationThread.start();

		// timer, sensors, checkbox and overlay
		final Handler handler = new Handler();

		t.schedule(new TimerTask() {
			public void run() {
				handler.post(new Runnable() {
					public void run() {
						Log.i("run", "outside");
						if (timerOn == true) {
							Log.i("run", "inside");
							// compass.updateSound();
							compass.newThread();
						}
					}
				});
			}
		}, 1000, 3000);

		m_myDynamicSpinner = (Spinner) findViewById(R.id.spinner1);
		m_adapterForSpinner = new ArrayAdapter<CharSequence>(this,
				android.R.layout.simple_spinner_item);
		m_adapterForSpinner
				.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		m_myDynamicSpinner.setAdapter(m_adapterForSpinner);
		m_myDynamicSpinner
				.setOnItemSelectedListener(new OnItemSelectedListener() {

					public void onItemSelected(AdapterView<?> arg0, View arg1,
							int arg2, long arg3) {
						selectedProjectIndex = arg2;
						Log.i("Selcted projct = ", Integer
								.toString(selectedProjectIndex));
					}

					public void onNothingSelected(AdapterView<?> arg0) {

					}
				});

		mSeekBar = (SeekBar) findViewById(R.id.seek);
		mSeekBar.setOnSeekBarChangeListener(listener);
		mProgressText = (TextView) findViewById(R.id.progress);
		mProgressText.setText("Load " + mSeekBar.getProgress() + " markers");

		marker = (TextView) findViewById(R.id.markerText);
		marker.setText("Preview sounds");

		final CheckBox checkBox = (CheckBox) findViewById(R.id.checkBox);
		checkBoxGlobal = checkBox;

		final CheckBox checkBox2 = (CheckBox) findViewById(R.id.checkBox2);
		checkBoxGlobal2 = checkBox2;

		final CheckBox markerCheck = (CheckBox) findViewById(R.id.markerCheck);
		markerCheck.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				// Perform action on clicks
				if (markerCheck.isChecked()) {
					marker.setText("Preview images");
					filetype = "Image";
				} else {
					marker.setText("Preview sounds");
					filetype = "Sound";
				}
			}
		});

		mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

		positionOverlay = new drawLineVector(cntxt);
		mapOverlays = myMapView.getOverlays();
		mapOverlays.add(positionOverlay);

	} // End of onCreate() method.

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Communication Thread begins here. Fetches projects and markers from the
	 * servlet.
	 */
	public void run() {

		URL ServletURL;

		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

		/**
		 * Request for a list of projects from servlet only once when the
		 * activity starts. Controlled by global boolean :- getProjectsFlag
		 */
		if (getProjectsFlag) {

			if (projectList != null)
				projectList = null;
			projectList = new ArrayList<URProject>();

			try {
				ServletURL = new URL(servletAddress + "param1=FetchProjects");
				URLConnection connection = ServletURL.openConnection();
				InputStream is = connection.getInputStream();
				BufferedReader in = new BufferedReader(
						new InputStreamReader(is));

				String line = null;
				while ((line = in.readLine()) != null) {
					JSONTokener tokenline = new JSONTokener(line);
					JSONObject jsonObject = new JSONObject(tokenline);
					projectList.add(new URProject((String) jsonObject
							.get("Project Name"), Integer
							.parseInt((String) jsonObject.get("Project ID"))));
				}

				/**
				 * Add projectList to spinner widget in UI
				 */
				runOnUiThread(new Runnable() {
					public void run() {
						m_adapterForSpinner.clear();
						for (int i = 0; i < projectList.size(); i++)
							m_adapterForSpinner.add(projectList.get(i)
									.getProjectName());
						myMapView.postInvalidate();
					}
				});
				getProjectsFlag = false;

			} catch (MalformedURLException urle) {
				urle.printStackTrace();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			} catch (JSONException jsone) {
				jsone.printStackTrace();
			}
		} else {

			/**
			 * Clear all existing markers from managedOverlay and update map
			 */
			runOnUiThread(new Runnable() {

				public void run() {
					overlayManager.getOverlay("DatabaseMarkers")
							.getOverlayItems().clear();
					overlayManager.populate();
				}
			});

			/**
			 * Get header i.e markersetMax value and update next and previous
			 * buttons accordingly
			 */
			// selectedProjectIndex = 0;
			try {
				ServletURL = new URL(servletAddress
						+ "param1=FetchMarkers&TopLeftLat="
						+ Integer.toString(topLeftLocation.getLatitudeE6())
						+ "&TopLeftLng="
						+ Integer.toString(topLeftLocation.getLongitudeE6())
						+ "&BottomRightLat="
						+ Integer.toString(bottomRightLocation.getLatitudeE6())
						+ "&BottomRightLng="
						+ Integer
								.toString(bottomRightLocation.getLongitudeE6())
						+ "&MaxMarkers="
						+ Integer.toString(maxNumberOfMarkers)
						+ "&ProjectId="
						+ Integer.toString(projectList
								.get(selectedProjectIndex).getProjectId())
						+ "&Filetype=" + filetype);
				// TODO projectindex can cause a crash...null pointer
				Log.i(TAG + "  Servlet URL = ", ServletURL.toString());
				URLConnection connection = ServletURL.openConnection();
				InputStream is = connection.getInputStream();
				BufferedReader in = new BufferedReader(
						new InputStreamReader(is));

				String line = null;
				if (mylist != null)
					mylist = null;
				mylist = new ArrayList<ManagedOverlayItem>();
				int count = 0;
				while ((line = in.readLine()) != null) {
					count++;
					JSONTokener tokenline = new JSONTokener(line);
					JSONObject jsonObject = new JSONObject(tokenline);
					ManagedOverlayItem overlayItem = new ManagedOverlayItem(
							new GeoPoint(Integer.parseInt((String) jsonObject
									.get("Latitude")), Integer
									.parseInt((String) jsonObject
											.get("Longitude"))), Integer
									.toString(count), (String) jsonObject
									.get("URL"));
					mylist.add(overlayItem);
				}

				runOnUiThread(new Runnable() {

					public void run() {
						managedOverlay.addAll(mylist);
						// preserve the previous position of itemred.
						if (itemred != null)
							managedOverlay.add(itemred);
						overlayManager.populate();
					}
				});
			} catch (MalformedURLException urle) {
				urle.printStackTrace();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			} catch (JSONException jsone) {
				jsone.printStackTrace();
			}
			Log.i(TagThread, "Done Fetching. Thread dying.");

		} // run this code only when the getProjectsFlag is false..

	} // End of Background thread operation that interacts with the server

	/**
	 * Code to render sound
	 * 
	 * @author ryannikolaidis
	 */

	public class drawLineVector extends Overlay {

		private Location location;

		// private GeoPoint locationPoint;

		public drawLineVector(Context cntxt) {
		}

		public Location getLocation() {
			return location;
		}

		public void setLocation(Location location) {
			this.location = location;
			// Double latitude = location.getLatitude() * 1E6;
			// Double longitude = location.getLongitude() * 1E6;
			// locationPoint = new GeoPoint(latitude.intValue(), longitude
			// .intValue());
		}

		@Override
		public void draw(Canvas canvas, MapView mapView, boolean shadow) {

			if (true) {
				// [ ... draw stuff here ... ]

				Projection projection = mapView.getProjection();

				Double latitude = lat2 * 1E6;
				// Double latitude = 33936118.;

				Double longitude = lng2 * 1E6;
				// Double longitude = -84571181.;

				Double latitudeStart = myLat * 1E6;
				Double longitudeStart = myLng * 1E6;
				GeoPoint geopoint = new GeoPoint(latitude.intValue(), longitude
						.intValue());
				GeoPoint geopoint2 = new GeoPoint(latitudeStart.intValue(),
						longitudeStart.intValue());

				Point point = new Point();
				projection.toPixels(geopoint, point);

				Point point2 = new Point();
				projection.toPixels(geopoint2, point2);

				Paint paint = new Paint();

				// initialize paint vector style

				paint.setColor(255);
				paint.setStrokeWidth((float) 2.25);

				if (checkBoxGlobal.isChecked()) {
					paint.setAlpha(255);
					timerOn = true;
				} else {
					paint.setAlpha(0);
					timerOn = false;
				}

				canvas.drawLine(point2.x, point2.y, point.x, point.y, paint);

			}

			super.draw(canvas, mapView, shadow);
		}

		@Override
		public boolean onTap(GeoPoint point, MapView mapView) {
			return false;
		}

	}

	private final SensorListener mListener = new SensorListener() {

		public void onSensorChanged(int sensor, float[] values) {
			if (Config.LOGD)
				// Log.d(TAG, "sensorChanged (" + values[0] + ", " + values[1]
				// + ", " + values[2] + ")");
				mValues = values;

			double degreesToRad = mValues[0] * 8 * ((float) Math.PI) / 180;

			double zoomLevel = 0.;

			if (myMapView.getZoomLevel() > 15 || myMapView.getZoomLevel() == 8) {
				zoomLevel = Math.pow(250., 16. / myMapView.getZoomLevel()) / 250.;
			} else if (myMapView.getZoomLevel() > 7
					&& myMapView.getZoomLevel() < 16
					|| myMapView.getZoomLevel() == 6) {
				zoomLevel = Math.pow(250., 16. / myMapView.getZoomLevel()) / 80.;
			} else if (myMapView.getZoomLevel() == 7) {
				zoomLevel = 900;
			} else if (myMapView.getZoomLevel() < 6
					&& myMapView.getZoomLevel() > 4) {
				zoomLevel = 1800;
			} else if (myMapView.getZoomLevel() > 1
					&& myMapView.getZoomLevel() < 5) {
				zoomLevel = 6600;
			} else if (myMapView.getZoomLevel() == 1) {
				zoomLevel = 24000;
			}

			double calcValue = zoomLevel * mValues[1] * .00005
					* Math.sin(degreesToRad);
			double calcValue2 = zoomLevel * mValues[1] * .5 * .0001
					* Math.cos(degreesToRad);

			if (!checkBoxGlobal2.isChecked()) {
				myLat = itemred.getPoint().getLatitudeE6() / 1E6;
				myLng = itemred.getPoint().getLongitudeE6() / 1E6;

				lat2 = myLat + calcValue;
				lng2 = myLng + calcValue2;
				Compass.setData(myLat, myLng, lat2, lng2);
				myMapView.invalidate();
			}

		}

		public void onAccuracyChanged(int sensor, int accuracy) {
		}
	};

	@Override
	protected void onResume() {
		if (Config.LOGD)
			Log.d(TAG, "onResume");
		super.onResume();
		mSensorManager.registerListener(mListener,
				SensorManager.SENSOR_ORIENTATION,
				SensorManager.SENSOR_DELAY_GAME);
		// Log.i(TAG, "Hey...Resuming");
	}

	@Override
	protected void onStop() {
		timerOn = false;
		if (Config.LOGD)
			Log.d(TAG, "onStop");
		mSensorManager.unregisterListener(mListener);
		super.onStop();
	}

	// End sound rendering process...via timer
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_BACK:
			checkBoxGlobal.setChecked(false);
			getProjectsFlag = true;
			m_adapterForSpinner.clear();
			break;
		case KeyEvent.KEYCODE_HOME:
			checkBoxGlobal.setChecked(false);
			getProjectsFlag = true;
			break;
		default:
			break;
		}
		return super.onKeyDown(keyCode, event);
	}

}